package system

import (
	consts2 "MoSkeleton/config/consts"
	"MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/dto/request"
	"MoSkeleton/framework/core/models"
	"MoSkeleton/framework/core/moerrors"
	"MoSkeleton/framework/mo_gorm"
	"MoSkeleton/framework/utils"
	"MoSkeleton/models/system"
	"MoSkeleton/services/base"
	"MoSkeleton/web/pogos"
	s1 "MoSkeleton/web/pogos/request/system"
	system2 "MoSkeleton/web/pogos/respose/system"
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
	"github.com/samber/lo"
	"gorm.io/gorm"
	"strconv"
)

type SysDeptService struct {
	base.BaseService
}

func (d *SysDeptService) GetDeptAll(c *gin.Context, deptReq *s1.SysDeptPageReq) (*[]*system2.SysDeptItemResp, error) {
	db := d.getDeptVo(nil, deptReq)
	result := make([]*system2.SysDeptItemResp, 0)
	//数据范围过滤
	d.QueryDataScope(c, db)
	defer func() {
		mo_gorm.ResetDataScopeCount(db)
	}()
	if err := db.Find(&result).Error; err != nil {
		return nil, err
	}
	return &result, nil
}

func (d *SysDeptService) CountDeptByReq(c *gin.Context, deptReq *s1.SysDeptPageReq) (int64, error) {
	db := d.getDeptVo(nil, deptReq)
	d.QueryDataScope(c, db)
	defer func() {
		mo_gorm.ResetDataScopeCount(db)
	}()
	var count int64
	result := db.Count(&count)
	return count, result.Error
}

func (d *SysDeptService) GetExcludeDeptAndChildren(idStr string) (*[]system2.SysDeptItemResp, error) {
	result := make([]system2.SysDeptItemResp, 0)
	err := base.BaseDb.Model(system.SysDept{}).Where("id not in (?)", idStr).Where("del_flag = ?", "0").
		Where("not find_in_set(?, ancestors)", idStr).Order("parent_id, order_num").Find(&result).Error
	return &result, err
}

func (d *SysDeptService) AddDept(addReq *s1.SysDeptAddReq) (bool, error) {
	parentId := addReq.ParentId
	if parentDepts, err1 := d.withParentNameDept(nil, []uint64{parentId}); err1 != nil {
		return false, err1
	} else if len(*parentDepts) == 1 && (*parentDepts)[0].Status != consts2.U_DEPT_NORMAL {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "部门停用，不允许新增")
	} else {
		sysDept := &system.SysDept{}
		if err := copier.Copy(sysDept, addReq); err != nil {
			return false, err
		}
		parentAncestors := "0"
		if len(*parentDepts) > 0 {
			parentAncestors = (*parentDepts)[0].Ancestors
		}
		sysDept.Ancestors = parentAncestors + "," + strconv.FormatUint(addReq.ParentId, 10)
		omitFields := "status, del_flag"
		result := base.BaseDb.Omit(omitFields).Create(sysDept)
		return result.RowsAffected == 1, result.Error
	}
}

func (d *SysDeptService) DeleteById(c *gin.Context, id uint64) (bool, error) {
	dept := &system.SysDept{MoModel: models.MoModel{ID: id}}
	if hasChildren, err := dept.HasChildrenDept(); err != nil {
		return false, err
	} else if hasChildren {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "存在子部门,不允许删除")
	}
	if isExists, err := sysUserService.CheckDeptExistsUser(id); err != nil {
		return false, err
	} else if isExists {
		return false, moerrors.NewValidateError(consts.CommCreatFailCode, "部门下存在用户,不允许删除")
	}
	if err := d.CheckDeptDataScope(c, id); err != nil {
		return false, err
	}
	result := base.BaseDb.Delete(dept)
	return result.RowsAffected == 1, result.Error
}

func (d *SysDeptService) GetDeptById(c *gin.Context, id uint64) (*system2.SysDeptItemResp, error) {
	if err := d.CheckDeptDataScope(c, id); err != nil {
		return nil, err
	}
	dept := &system2.SysDeptItemResp{}
	if err := base.BaseDb.Model(&system.SysDept{}).Where("id = ?", id).First(dept).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	return dept, nil
}

func (d *SysDeptService) GetDeptIdListByRoleId(roleId uint64) (*[]uint64, error) {
	sysRole := &system.SysRole{MoModel: models.MoModel{ID: roleId}}
	sysRole, err := sysRole.GetById(nil, roleId)
	if err != nil {
		return nil, err
	}
	deptIds, err := new(system.SysDept).GetDeptIdListByRoleId(nil, roleId, sysRole.DeptCheckStrictly)
	if err != nil {
		return nil, err
	}
	return &deptIds, nil
}

func (d *SysDeptService) GetDeptByUserDetailId(userId uint64) (*system2.SysUserDeptItemResp, error) {
	selectFields := "d.id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status"
	db := base.BaseDb.Select(selectFields).Table("sys_dept d")
	db.Joins("left join sys_user u  on u.dept_id = d.id")
	db.Where("u.id = ?", userId)
	itemResp := new(system2.SysUserDeptItemResp)
	if err := db.Find(&itemResp).Error; err != nil {
		return nil, err
	}
	return itemResp, nil
}
func (d *SysDeptService) GetDeptTreeList(c *gin.Context, deptReq *s1.SysDeptPageReq) ([]*pogos.TreeSelect, error) {
	deptItems, err := d.GetDeptAll(c, deptReq)
	if err != nil {
		return nil, err
	}
	tss := d.buildDeptTreeSelect(deptItems)
	return tss, err
}

func (d *SysDeptService) UpdateDeptById(c *gin.Context, updateReq *s1.SysDeptUpdateReq) (ok bool, err error) {
	if err := d.CheckDeptDataScope(c, updateReq.ID); err != nil {
		return false, err
	}
	sysDept := &system.SysDept{}

	if err = copier.Copy(sysDept, updateReq); err != nil {
		ok = false
		return
	}
	err = base.BaseDb.Transaction(func(tx *gorm.DB) error {
		if *(sysDept.Status) == consts2.U_DEPT_DISABLE {
			if count, err := sysDept.GetNormalChildrenDeptCount(tx); err != nil {
				return err
			} else if count > 0 {
				return moerrors.NewValidateError(consts.CommCreatFailCode, "存在子部门,不允许停用")
			}
		}

		idAndParentId := []uint64{updateReq.ParentId, updateReq.ID}
		var parentDepts *[]system2.WithParentDeptNameResp

		if parentDepts, err = d.withParentNameDept(tx, idAndParentId); err != nil {
			return err
		}
		parentDept, oldDept := d.splitDeptAndParent(parentDepts, updateReq.ParentId, updateReq.ID)
		if parentDept != nil && oldDept != nil && oldDept.ParentId != updateReq.ParentId {
			newAncestors := parentDept.Ancestors + "," + strconv.FormatUint(parentDept.ID, 10)
			oldAncestors := oldDept.Ancestors
			sysDept.Ancestors = newAncestors
			if err := sysDept.UpdateChildrenAncestors(tx, newAncestors, oldAncestors); err != nil {
				return err
			}
		}

		if err := d.GetDb(tx).Omit("del_flag").Updates(sysDept).Error; err != nil {
			return err
		}
		if *(sysDept.Status) == consts2.U_DEPT_NORMAL && sysDept.Ancestors != "" {
			// 如果该部门是启用状态，则启用该部门的所有上级部门
			if err := sysDept.UpdateParentDeptStatusNormal(tx); err != nil {
				return err
			}
		}
		ok = true
		return nil
	})
	return
}

/*
检测同部门下是否有重名

select id
from sys_dept d
where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
*/
func (d *SysDeptService) CheckDeptNameUnique(uniqueReq *s1.DeptUniqueReq, idReq *request.IDRequest) (bool, error) {
	dept := &system.SysDept{}
	if err := base.BaseDb.Select("id").Where("dept_name = ? and parent_id = ? and del_flag = ?", uniqueReq.DeptName, uniqueReq.ParentId, "0").First(dept).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return true, nil
		}
		return false, err
	}
	return idReq == nil || dept.ID == idReq.ID, nil
}

func (d *SysDeptService) CheckDeptDataScope(c *gin.Context, id uint64) error {
	userId := utils.GetUserIdFromGin(c)
	if !system.IsUserAdmin(userId) && id > 0 {
		deptReq := new(s1.SysDeptPageReq)
		count, err := d.CountDeptByReq(c, deptReq)
		if err != nil {
			return err
		}
		if count == 0 {
			return moerrors.NewValidateError(consts.DataScopeLimitedCode, "没有权限访问部门数据！")
		}
	}
	return nil
}

/*
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,

		(select dept_name from sys_dept where dept_id = d.parent_id) parent_name
	from sys_dept d
	where d.dept_id = #{deptId}
*/
func (d *SysDeptService) withParentNameDept(tx *gorm.DB, ids []uint64) (*[]system2.WithParentDeptNameResp, error) {
	parentDeptNames := make([]system2.WithParentDeptNameResp, 0)
	selectFields := "d.id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status,(select dept_name from sys_dept where id = d.parent_id) parent_name"
	result := d.GetDb(tx).Table("sys_dept d").Select(selectFields).Where("d.id in  (?)", ids).Find(&parentDeptNames)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, result.Error
	}
	return &parentDeptNames, nil
}

func (d *SysDeptService) splitDeptAndParent(parentDeptNames *[]system2.WithParentDeptNameResp, parentId, id uint64) (parentDept, oldDept *system2.WithParentDeptNameResp) {
	if len(*parentDeptNames) == 0 {
		return nil, nil
	}
	for _, v := range *parentDeptNames {
		if v.ID == parentId {
			parentDept = &v
		}
		if v.ID == id {
			oldDept = &v
		}
	}
	return
}

func (d *SysDeptService) buildDeptTreeSelect(deptItemsResp *[]*system2.SysDeptItemResp) []*pogos.TreeSelect {
	deptTrees := d.buildDeptTree(deptItemsResp)
	return lo.Map(*deptTrees, func(item *system2.SysDeptItemResp, index int) *pogos.TreeSelect {
		return pogos.NewTreeSelectByDept(item)
	})
}

func (d *SysDeptService) buildDeptTree(deptItemsResp *[]*system2.SysDeptItemResp) *[]*system2.SysDeptItemResp {
	result := make([]*system2.SysDeptItemResp, 0)
	deptIds := lo.Map(*deptItemsResp, func(item *system2.SysDeptItemResp, index int) uint64 {
		return item.ID
	})
	lo.ForEach(*deptItemsResp, func(item *system2.SysDeptItemResp, index int) {
		// 如果是顶级节点, 遍历该父节点的所有子节点
		if !lo.Contains(deptIds, item.ParentId) {
			d.recursionFn(deptItemsResp, item)
			result = append(result, item)
		}
	})
	if len(result) == 0 {
		result = *deptItemsResp
	}
	return &result
}

/**
 * 递归列表
 */
func (d *SysDeptService) recursionFn(deptItemsResp *[]*system2.SysDeptItemResp, deptItem *system2.SysDeptItemResp) {
	children := d.getChildren(deptItemsResp, deptItem)
	if children == nil {
		return
	}
	deptItem.Children = children
	lo.ForEach(*deptItem.Children, func(item *system2.SysDeptItemResp, index int) {
		d.recursionFn(deptItemsResp, item)
	})
}

/*
得到子节点列表
*/
func (d *SysDeptService) getChildren(deptItemsResp *[]*system2.SysDeptItemResp, rootDept *system2.SysDeptItemResp) *[]*system2.SysDeptItemResp {
	if deptItemsResp == nil {
		return nil
	}
	children := make([]*system2.SysDeptItemResp, 0)
	lo.ForEach(*deptItemsResp, func(item *system2.SysDeptItemResp, index int) {
		if item.ParentId != 0 && item.ParentId == rootDept.ID {
			children = append(children, item)
		}
	})
	if len(children) == 0 {
		return nil
	}
	return &children
}

/*
select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time
from sys_dept d

	 where d.del_flag = '0'
			<if test="deptId != null and deptId != 0">
				AND dept_id = #{deptId}
			</if>
	        <if test="parentId != null and parentId != 0">
				AND parent_id = #{parentId}
			</if>
			<if test="deptName != null and deptName != ''">
				AND dept_name like concat('%', #{deptName}, '%')
			</if>
			<if test="status != null and status != ''">
				AND status = #{status}
			</if>
			order by d.parent_id, d.order_num
*/
func (d *SysDeptService) getDeptVo(tx *gorm.DB, deptReq *s1.SysDeptPageReq) *gorm.DB {
	db := d.GetDb(tx).Model(system.SysDept{}).Where("del_flag = ?", "0")
	if deptReq.ID != 0 {
		db = db.Where("dept_id = ?", deptReq.ID)
	}
	if deptReq.ParentId != 0 {
		db = db.Where("parent_id = ?", deptReq.ParentId)
	}
	if deptReq.DeptName != "" {
		db = db.Where("dept_name like ?", "%"+deptReq.DeptName+"%")
	}
	if deptReq.Status != "" {
		db = db.Where("status = ?", deptReq.Status)
	}
	db.Order("parent_id, order_num")
	return db
}
